#pragma once
#ifndef _AS_SESSION_H_
#define _AS_SESSION_H_

#include "net_addr.h"
#include "as_socket.h"
#include "object_pool.h"

#ifdef _WIN32
#else
#include <sys/epoll.h>
#endif

class ByteBuffer;
namespace as
{
namespace inet
{

#ifdef _WIN32
enum IOType : uint32_t
{
	NONE,
	Read,
	Write,
	Accept,
	Connect,
};

#else
enum IOType : uint32_t
{
	NONE,
	Read = EPOLLIN | EPOLLPRI | EPOLLET,
	Write = EPOLLOUT,
	Accept,
};
#endif

constexpr int IO_CONTEXT_BUF_SIZE = 1024;
#ifdef _WIN32

	struct IOContext {
		OVERLAPPED overlapped{};
		WSABUF wsaBuf{ IO_CONTEXT_BUF_SIZE, buffer };
		CHAR buffer[IO_CONTEXT_BUF_SIZE]{};
		as::inet::IOType type{};
		socket_fd socket = SOCKET_ERR_FD;
		std::shared_ptr<as::inet::AsSession> ioSession;
		DWORD nBytes = 0;
		void reset()
		{
			clear();
			socket = SOCKET_ERR_FD;
			ioSession.reset();
		}

		void clear()
		{
			if (wsaBuf.buf != NULL)
				ZeroMemory(wsaBuf.buf, IO_CONTEXT_BUF_SIZE);
			ZeroMemory(buffer, IO_CONTEXT_BUF_SIZE);
			ZeroMemory(&overlapped, sizeof(overlapped));
			type = IOType::NONE;
		}
	};
#else
	struct IOContext : public epoll_event 
	{
		IOType type{};
		char buffer[IO_CONTEXT_BUF_SIZE]{};
		socket_fd socket = SOCKET_ERR_FD;
		int sid = 0;
		std::shared_ptr<as::inet::AsSession> ioSession;
		void reset()
		{
			memset(this, '0', sizeof(IOContext));
		}

		void clear()
		{
			reset();
		}
	};
#endif

typedef std::unique_ptr<as::inet::AsSocket> AsSocketPtr;
class AsSession : public std::enable_shared_from_this<as::inet::AsSession>
{
public:
	friend class AsAcceptor;
    AsSession();
    ~AsSession();

    void reset();

    AsSession* init();
    AsSession* init(socket_fd fd);    

	AsSession* listen(const NetAddr &addr);
	AsSession* connect(const NetAddr &addr);
    void close();

    AsSession* set_sid(int sid) { m_sid = sid; return this; }
    int get_sid() const { return m_sid; }
#ifdef _WIN32
    AsSession* set_client_addr(SOCKADDR_IN *clientAddr);
#else
	AsSession* set_client_addr(sockaddr_in *clientAddr);
#endif

    socket_fd get_socket_fd(); 

    //send msg
    int send_packet(std::string_view buf);

    IOContext* pop_io_context();
    void push_io_context(IOContext *io);

protected:
	int out_packet();

private:
    ObjectPool<IOContext> m_ioContextPool;
	std::mutex m_writeLock;
	std::unique_ptr<ByteBuffer> m_writeBuf;
    AsSocketPtr m_socket;
    NetAddr m_addr; 
    IOType m_ioType;
    int m_sid = 0;
};


}
}

#endif